/*
 * 帮助方法集合，所有几何计算只涉及到水平/竖直计算
 * @Author: tongbin.xtb
 * @Date: 2018-11-30 10:12:20
 * @Last Modified by: tongbin.xtb
 * @Last Modified time: 2018-12-03 21:12:45
 */

export function drawPath(group, path, style) {
  const keyShape = group.addShape('path', {
    attrs: {
      ...style,
      path
    }
  });
  return keyShape;
}

export function dim2Distance(p1, p2) {
  return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}

/**
 * 计算节点锚点集合，节点边界的各个中点
 * @param {object} bbox 节点边界模型
 * @return {array} 锚点集合
 */
export function getBBoxAnchors(bbox) {
  return [
    { x: bbox.centerX, y: bbox.minY },
    { x: bbox.maxX, y: bbox.centerY },
    { x: bbox.centerX, y: bbox.maxY },
    { x: bbox.minX, y: bbox.centerY }
  ];
}

/**
 * 计算离point最近的bbox的锚点
 * @param {object} point 点
 * @param {object} bbox 节点边界模型
 * @return {object} point, index 描述锚点
 */
export function getPointAnchor(point, bbox) {
  const points = getBBoxAnchors(bbox);
  let minDist = Infinity;
  let result;
  let index;
  points.forEach((p, i) => {
    const dist = dim2Distance(point, p);
    if (dist < minDist) {
      result = p;
      minDist = dist;
      index = i;
    }
  });
  return {
    point: result,
    index
  };
}

export function getBBoxCenter(node) {
  if (!node) {
    return {
      x: undefined,
      y: undefined
    };
  }
  const bbox = node.getBBox();
  return {
    x: bbox.centerX,
    y: bbox.centerY
  };
}

export function middleInLine(cp, index) {
  return {
    x: (cp[index].x + cp[index + 1].x) / 2,
    y: (cp[index].y + cp[index + 1].y) / 2
  };
}

/**
 * 根据开始节点和结束节点，插入中间值
 * @param {object} start 开始节点
 * @param {object} end 结束节点
 * @return {array} 需要插入的节点
 */
export function insertPoint({ point: start, index: startIndex }, { point: end, index: endIndex }) {
  if (start.x === end.x || start.y === end.y) {
    return [ start, end ];
  }
  if ((startIndex === 2 && endIndex === 0) || (startIndex === 0 && endIndex === 2)) {
    return [ start, {
      x: start.x,
      y: (start.y + end.y) / 2
    }, {
      x: end.x,
      y: (start.y + end.y) / 2
    }, end ];
  }
  if (((startIndex === 2 || startIndex === 0) && (endIndex === 3 || endIndex === 1))
  ) {
    return [ start, {
      x: start.x,
      y: end.y
    }, end ];
  }
  if (((startIndex === 1 || startIndex === 3) && (endIndex === 2 || endIndex === 0))) {
    return [ start, {
      x: end.x,
      y: start.y
    }, end ];
  }
  return [ start, {
    x: (start.x + end.x) / 2,
    y: start.y
  }, {
    x: (start.x + end.x) / 2,
    y: end.y
  }, end ];
}

/**
 * [controlPoints[index], controlPoints[index + 1]] 是不是竖直方向。
 * 两个点的x值相同说明是竖直方向
 * @param {array} controlPoints 控制点数组
 * @param {number} index 控制点数组下标
 * @return {bool} 是否垂直
 */
export function isVertical(controlPoints, index) {
  return controlPoints[index].x === controlPoints[index + 1].x;
}

/**
 * point 是不是在 [controlPoints[index], controlPoints[index + 1]] 这个线段上。
 * 只判定竖直和水平方向
 * @param {object} point 包含x, y的点
 * @param {array} controlPoints 控制点数组
 * @param {number} index 控制线的下标
 * @return {number} 点是不是在线段上
 */
export function pointInLine(point, controlPoints, index) {
  if (isVertical(controlPoints, index)) {
    return point.x === controlPoints[index].x &&
      (point.y - controlPoints[index].y) * (point.y - controlPoints[index + 1].y) <= 0;
  }
  return point.y === controlPoints[index].y &&
    (point.x - controlPoints[index].x) * (point.x - controlPoints[index + 1].x) <= 0;
}

/**
 * p1到p2的距离，简易计算
 * @param {objet} p1 包含x, y的点
 * @param {object} p2 包含x, y的点
 * @return {number} 点距
 */
export function pointDistance(p1, p2) {
  return Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
}

/**
 *
 * @param {object} point 所在点
 * @param {array} controlPoints 线的控制点
 * @return {object} 垂点，hover的下标，是否垂直走向
 */
export function getHoverStatus(point, controlPoints) {
  let distance = Infinity;
  let index;
  let vp; // 垂点
  let lv; // 点所在的线是否是垂直方向
  for (let i = 0; i < controlPoints.length - 1; i++) {
    const lineVertical = isVertical(controlPoints, i);
    let verticalPoint;
    if (lineVertical) {
      verticalPoint = {
        x: controlPoints[i].x,
        y: point.y
      };
    } else {
      verticalPoint = {
        x: point.x,
        y: controlPoints[i].y
      };
    }
    if (pointInLine(verticalPoint, controlPoints, i)) {
      const d = pointDistance(point, verticalPoint);
      if (distance > d) {
        distance = d;
        index = i;
        vp = verticalPoint;
        lv = lineVertical;
      }
    }
  }
  return {
    verticalPoint: vp,
    index,
    vertical: lv
  };
}

function pointInSameSide(bbox, p1, p2) {
  return (bbox.centerX - p1.x) * (bbox.centerX - p2.x) > 0 ||
    (bbox.centerY - p1.y) * (bbox.centerY - p2.y) > 0;
}

export function getControlPointsBySourceAndTarget(source, target, startAnchor) {
  const targetCenter = getBBoxCenter(target);
  let sourcePoint = source.getLinkPoints(targetCenter)[0];
  if (startAnchor) {
    const userPreferedAnchor = getBBoxAnchors(source.getBBox())[startAnchor];
    if (pointInSameSide(source.getBBox(), userPreferedAnchor, {
      x: target.getBBox().centerX,
      y: target.getBBox().centerY
    })) {
      sourcePoint = userPreferedAnchor;
    }
  }
  sourcePoint = getPointAnchor(sourcePoint, source.getBBox());
  // const sourceCenter = getBBoxCenter(source);
  let targetPoint = target.getLinkPoints(sourcePoint.point)[0];
  targetPoint = getPointAnchor(targetPoint, target.getBBox());
  return { sourcePoint, targetPoint };
}
/**
 *
 * @param {object} bbox 节点的边界
 * @param {array} controlPoints 控制点集合
 * @param {number} index 控制线段下标
 * @return {number} -1: 在左侧或者上侧；1: 在右侧或者下侧； 0: 在线上
 */
export function bboxSideByLine(bbox, controlPoints, index) {
  const lineVertical = isVertical(controlPoints, index);
  if (lineVertical) {
    if (bbox.minX > controlPoints[index].x) {
      return 1;
    } else if (bbox.maxX < controlPoints[index].x) {
      return -1;
    }
    return 0;
  }
  if (bbox.minY > controlPoints[index].y) {
    return 1;
  } else if (bbox.maxY < controlPoints.y) {
    return -1;
  }
  return 0;
}

/**
 *
 * @param {object} point 需要计算垂点的点
 * @param {array} controlPoints 控制点
 * @param {number} index 线段下标
 * @return {object} 垂点
 */
export function getVerticalPoint(point, controlPoints, index) {
  const vertical = isVertical(controlPoints, index);
  if (vertical) {
    return {
      x: controlPoints[index].x,
      y: point.y
    };
  }
  return {
    x: point.x,
    y: controlPoints[index].y
  };
}

/**
 * 求在盒模型的另一边
 * @param {number} index 0~3 目前在盒模型上的位置
 * @return {number} index相反方向的锚点下标
 */
export function oppositeAnchorIndex(index) {
  let oppsite;
  switch (index) {
    case 0:
      oppsite = 2;
      break;
    case 1:
      oppsite = 3;
      break;
    case 3:
      oppsite = 1;
      break;
    default:
      oppsite = 0;
  }
  return oppsite;
}
